package org.alis.rpc.route.impl;

import com.google.common.collect.Sets;
import org.alis.rpc.common.protocol.RpcProtocol;
import org.alis.rpc.route.AbstractRpcLoadBalance;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * RpcLoadBalanceLRU desc: 最近最少使用
 *
 * @author luochuan
 * @date 2020/10/9 14:26
 */
public class RpcLoadBalanceLRU<V> extends AbstractRpcLoadBalance<V> {

    private Map<String, LinkedHashMap<RpcProtocol, RpcProtocol>> jobLruMap =new ConcurrentHashMap<>();

    private long cacheValidTime = 0;

    @Override
    public RpcProtocol doRoute(String serviceKey, List<RpcProtocol> addressList) {
        // cache clear
        if (System.currentTimeMillis() > cacheValidTime) {
            jobLruMap.clear();
            cacheValidTime = System.currentTimeMillis() + 1000 * 60 * 60 * 24;
        }

        // init lru
        LinkedHashMap<RpcProtocol, RpcProtocol> lruHashMap = jobLruMap.get(serviceKey);
        if (lruHashMap == null) {
            /*
             * LinkedHashMap
             * a、accessOrder：ture=访问顺序排序（get/put时排序）/ACCESS-LAST；false=插入顺序排期/FIFO；
             * b、removeEldestEntry：新增元素时将会调用，返回true时会删除最老元素；
             *      可封装LinkedHashMap并重写该方法，比如定义最大容量，超出是返回true即可实现固定长度的LRU算法；
             */
            lruHashMap = new LinkedHashMap<>(16, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry<RpcProtocol, RpcProtocol> eldest) {
                    return super.size() > 1000;
                }
            };
            jobLruMap.putIfAbsent(serviceKey, lruHashMap);
        }

        // put new
        for (RpcProtocol address : addressList) {
            if (!lruHashMap.containsKey(address)) {
                lruHashMap.put(address, address);
            }
        }
        Set<RpcProtocol> tempSet = Sets.newHashSet(addressList);
        Iterator<RpcProtocol> iterator = lruHashMap.keySet().iterator();
        while(iterator.hasNext()){
            if(!tempSet.contains(iterator.next())){
                iterator.remove();
                lruHashMap.remove(iterator.next());
            }
        }

        // load
        RpcProtocol eldestKey = lruHashMap.entrySet().iterator().next().getKey();
        return lruHashMap.get(eldestKey);
    }



}